package org.tamal.spring;

import java.math.BigDecimal;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.sql.DataSource;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;


/**
 * This is the default implementation of service or business interface.
 * @author Tamal Kanti Nath
 */
@Repository
public final class ServiceImpl implements Service {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private DataSource dataSource;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    @Transactional("jpaTransactionManager")
    public <T> T findEntity(String query, Class<T> cls, Object... params) {
        TypedQuery<T> typedQuery = entityManager.createQuery(query, cls);
        for (int i = 0; i < params.length; ++i) {
            typedQuery.setParameter(i + 1, params[i]);
        }
        return typedQuery.getSingleResult();
    }

    @Override
    @Transactional("jpaTransactionManager")
    public <T> List<T> findEntities(String query, Class<T> cls,
            Object... params) {
        TypedQuery<T> typedQuery = entityManager.createQuery(query, cls);
        for (int i = 0; i < params.length; i += 2) {
            typedQuery.setParameter((String) params[i], params[i + 1]);
        }
        return typedQuery.getResultList();
    }

    @Override
    @SuppressWarnings("unchecked")
    @Transactional("hibernateTransactionManager")
    public <T> T findEntity(String query, Object... params) {
        Query q = sessionFactory.getCurrentSession().createQuery(query);
        for (int i = 0; i < params.length; ++i) {
            q.setParameter(i, params[i]);
        }
        return (T) q.uniqueResult();
    }

    @Override
    @SuppressWarnings("unchecked")
    @Transactional("hibernateTransactionManager")
    public <T> List<T> findEntities(String query, Object... params) {
        Query q = sessionFactory.getCurrentSession().createQuery(query);
        for (int i = 0; i < params.length; i += 2) {
            q.setParameter((String) params[i], params[i + 1]);
        }
        return q.list();
    }

    @Override
    @Transactional("jpaTransactionManager")
    public int updateCustomerAddress(String pan, String address) {
        String jpql = "update Customer set address=:address where pan=:pan";
        return entityManager.createQuery(jpql)
                .setParameter("pan", pan)
                .setParameter("address", address)
                .executeUpdate();
    }

    @Override
    @Transactional
    public void transferFund(Long source, Long target, BigDecimal amount) {
        String query = "update ACCOUNT set AMOUNT=AMOUNT-? where ID=?";
        int c1 = jdbcTemplate.update(query, amount, source);
        query = "update ACCOUNT set AMOUNT=AMOUNT+? where ID=?";
        int c2 = jdbcTemplate.update(query, amount, target);
        if (c1 != 1 || c2 != 1) {
            throw new RuntimeException();
        }
    }

}
